﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using Inscriptionem.Areas.Inscriptionem.Models.Enums;
using Inscriptionem.Areas.Inscriptionem.Models.Requests;
using Inscriptionem.Areas.Inscriptionem.Models.Views;
using Inscriptionem.Authorization;
using Inscriptionem.Controllers;
using Inscriptionem.Extensions;

namespace Inscriptionem.Areas.Inscriptionem.Controllers
{
    [InscriptionemAuthorize(MemberRole.Admin, MemberRole.Moderator, MemberRole.Writer)]
    public class PostController : Controller
    {
        private readonly ORM.DB DB;
        public PostController()
        {
            DB = new ORM.DB();
        }

        public ActionResult Index()
        {
            return View("Index", new PostCollectionViewModel());
        }

        public ActionResult Add()
        {
            return View("Add", new PostViewModel() { IsCommentable = true, IsSectionPinned = false, IsCategoryPinned = false, IsPublished = false, CreatedDate = DateTime.Now });
        }

        [HttpPost]
        [ValidateInput(false)]
        public ActionResult Add(PostRequestModel model)
        {
            var result = new PostViewModel();
            model.CopyTo(result);

            result.Error = ValidatePostAddModel(result);

            if (result.Error == PostError.None)
            {
                try
                {
                    DateTime date = DateTime.Now;

                    var post = new ORM.Post()
                                   {
                                       Title = result.Title ?? string.Empty,
                                       Content = result.Content ?? string.Empty,
                                       IsCommentable = result.IsCommentable,
                                       IsPublished = result.IsPublished,
                                       IsSectionPinned = result.IsSectionPinned,
                                       IsCategoryPinned = result.IsCategoryPinned,
                                       Member = DB.Members.First(m => m.ID == Member.Current.ID),
                                       ViewCount = 0,
                                       CreatedDate = result.CreatedDate.HasValue? result.CreatedDate.Value : date,
                                       ModifiedDate = date
                                   };

                    DB.AddToPosts(post);

                    if (model.CreateSet)
                    {
                        var set = new ORM.Set();
                        set.Posts.Add(post);

                        DB.AddToSets(set);
                    }
                    else if(model.SetID != 0)
                    {
                        var set = DB.Sets.FirstOrDefault(s => s.ID == model.SetID);
                        if (set != null)
                        {
                            set.Posts.Add(post);
                        }
                    }

                    // Categories
                    var categories = new List<ORM.Category>();
                    foreach (var item in result.Categories)
                    {
                        categories.Add(DB.Categories.First(c => c.ID == item));
                    }

                    foreach (var category in categories)
                    {
                        var postCategory = new ORM.PostCategory()
                                               {
                                                   Post = post,
                                                   Category = category
                                               };

                        DB.AddToPostCategories(postCategory);
                    }

                    // Tags
                    int firstCategoryID = result.Categories[0];
                    var language = DB.Categories.FirstOrDefault(c => c.ID == firstCategoryID).Section.Language;
                    var tags = (result.Tags ?? string.Empty).Split(new char[] {','}, StringSplitOptions.RemoveEmptyEntries).Select(t => t.Trim()).Distinct().ToList();

                    for (int i = 0; i < tags.Count; i++)
                    {
                        string currentTag = tags[i];
                        var tag = DB.Tags.FirstOrDefault(t => t.Name.ToLower() == currentTag.ToLower() && t.Language.ID == language.ID);

                        if (tag == null)
                        {
                            tag = new ORM.Tag()
                                      {
                                          Name = currentTag,
                                          Language = language
                                      };

                            DB.AddToTags(tag);
                        }

                        var postTag = new ORM.PostTags()
                                          {
                                              Tag = tag,
                                              Post = post
                                          };

                        DB.AddToPostTags(postTag);
                    }

                    DB.SaveChanges();
                }
                catch (Exception)
                {
                    result.Error = PostError.Other;
                }
            }

            return View("Add", result);
        }

        [HttpPost]
        [ValidateInput(false)]
        public ActionResult Edit(PostRequestModel model)
        {
            var result = new PostViewModel();
            model.CopyTo(result);

            // Not the best way to check if "Edit" button was pressed
            if (result.ID > 0 && string.IsNullOrWhiteSpace(result.Title) && string.IsNullOrWhiteSpace(result.Content)
                    && string.IsNullOrWhiteSpace(result.Tags) && (result.Categories == null|| result.Categories.Length == 0))
            {
                var post = DB.Posts.FirstOrDefault(p => p.ID == result.ID);
                if (post == null)
                {
                    // Post not found
                    return RedirectToAction("Index");
                }

                result.Title = post.Title;
                result.Content = post.Content;
                result.Categories = post.PostCategories.Select(c => c.Category.ID).ToArray();
                result.IsCommentable = post.IsCommentable;
                result.IsSectionPinned = post.IsSectionPinned;
                result.IsCategoryPinned = post.IsCategoryPinned;
                result.IsPublished = post.IsPublished;
                result.CreatedDate = post.CreatedDate;
                result.Tags = string.Join(", ", post.PostTags.Select(pt => pt.Tag).Select(t => t.Name));
                result.Member = post.Member;

                if (post.Set != null)
                {
                    result.SetID = post.Set.ID;
                }

                return View("Edit", result);
            }

            // Validate
            result.Error = ValidatePostEditModel(model);
            if (result.Error == PostError.None)
            {
                // Save changes
                try
                {
                    var post = DB.Posts.FirstOrDefault(p => p.ID == result.ID);

                    if (post == null)
                    {
                        return RedirectToAction("Index");
                    }

                    post.Title = result.Title ?? string.Empty;
                    post.Content = result.Content ?? string.Empty;
                    post.IsCommentable = result.IsCommentable;
                    post.IsSectionPinned = result.IsSectionPinned;
                    post.IsCategoryPinned = result.IsCategoryPinned;
                    post.IsPublished = result.IsPublished;
                    post.CreatedDate = result.CreatedDate.HasValue ? result.CreatedDate.Value : DateTime.Now;
                    post.Member = DB.Members.First(m => m.ID == result.MemberID);
                    post.ModifiedDate = DateTime.Now;

                    if (model.CreateSet || post.Set != null)
                    {
                        post.Set = null;
                    }

                    if (model.CreateSet)
                    {
                        var set = new ORM.Set();
                        set.Posts.Add(post);

                        DB.AddToSets(set);
                    }
                    else if (model.SetID != 0)
                    {
                        var set = DB.Sets.FirstOrDefault(s => s.ID == model.SetID);
                        if (set != null)
                        {
                            set.Posts.Add(post);
                        }
                    }

                    // Categories
                    var postCategories = post.PostCategories.Select(pc => pc.Category.ID).ToList();
                    var categoriesToKeep = result.Categories.Intersect(postCategories).ToList();
                    var categoriesToRemove = postCategories.Except(categoriesToKeep).ToList();

                    foreach (var item in categoriesToRemove)
                    {
                        var entity = DB.PostCategories.FirstOrDefault(pc => pc.Post.ID == post.ID && pc.Category.ID == item);
                        if (entity != null)
                        {
                            DB.PostCategories.DeleteObject(entity);
                        }
                    }

                    var categories = new List<ORM.Category>();
                    foreach (var item in result.Categories.Except(categoriesToRemove).Except(categoriesToKeep))
                    {
                        categories.Add(DB.Categories.First(c => c.ID == item));
                    }

                    foreach (var category in categories)
                    {
                        var postCategory = new ORM.PostCategory()
                        {
                            Post = post,
                            Category = category
                        };

                        DB.AddToPostCategories(postCategory);
                    }

                    // Tags
                    int firstCategoryID = result.Categories[0];
                    var language = DB.Categories.FirstOrDefault(c => c.ID == firstCategoryID).Section.Language;
                    var tags = (result.Tags ?? string.Empty).Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(t => t.Trim()).Distinct().ToList();

                    List<ORM.PostTags> unusedPostTags = post.PostTags.ToList();

                    for (int i = 0; i < tags.Count; i++)
                    {
                        string currentTag = tags[i];
                        var tag = DB.Tags.FirstOrDefault(t => t.Name.ToLower() == currentTag.ToLower() && t.Language.ID == language.ID);

                        if (tag == null)
                        {
                            tag = new ORM.Tag()
                            {
                                Name = currentTag,
                                Language = language
                            };

                            DB.AddToTags(tag);
                        }

                        var postTag = DB.PostTags.FirstOrDefault(pt => pt.Tag.ID == tag.ID && pt.Post.ID == post.ID);
                        if (postTag == null)
                        {
                            postTag = new ORM.PostTags()
                            {
                                Tag = tag,
                                Post = post
                            };

                            DB.AddToPostTags(postTag);
                        }
                        else
                        {
                            unusedPostTags.Remove(postTag);
                        }
                    }

                    unusedPostTags.ForEach(pt => DB.PostTags.DeleteObject(pt));

                    DB.SaveChanges();

                    return RedirectToAction("Index");
                }
                catch (Exception)
                {
                    result.Error = PostError.Other;
                }
            }

            return View("Edit", result);
        }

        [HttpPost]
        public ActionResult Delete(int ID)
        {
            if (ID <= 0)
            {
                return RedirectToAction("Index");
            }

            var post = DB.Posts.FirstOrDefault(p => p.ID == ID);
            if (post == null)
            {
                return RedirectToAction("Index");
            }

            PostError result = PostError.None;

            try
            {
                if (post.Comments.Any())
                {
                    result = PostError.PostHasContent;
                }
                else
                {
                    var postTags = DB.PostTags.Where(pt => pt.Post.ID == post.ID);
                    foreach (var postTag in postTags)
                    {
                        DB.PostTags.DeleteObject(postTag);
                    }

                    DB.Posts.DeleteObject(post);
                    DB.SaveChanges();

                    return RedirectToAction("Index");
                }
            }
            catch (Exception)
            {
                result = PostError.Other;
            }

            return View("Delete", result);
        }

        [HttpPost]
        public ActionResult Search(string Text, int Page)
        {
            if (Page < 0)
            {
                Page = 0;
            }

            var result = new PostSearchCollectionViewModel { Page = Page, Text = Text };

            return View("Search", result);
        }

        public ActionResult Page(int ID)
        {
            if (ID <= 0)
            {
                return RedirectToAction("Index");
            }

            return View("Index", new PostCollectionViewModel() {Page = ID});
        }

        #region Pagination
        public ActionResult Pagination(global::Inscriptionem.Models.Menu.Requests.PaginationRequestModel model)
        {
            return new MenuController().Pagination(model);
        }
        #endregion

        #region Implementation
        private PostError ValidatePostAddModel(PostRequestModel Model)
        {
            var editResult = ValidatePostEditModel(Model);
            if (editResult != PostError.None)
            {
                return editResult;
            }

            return PostError.None;
        }

        private PostError ValidatePostEditModel(PostRequestModel Model)
        {
            if (Model.Categories == null || Model.Categories.Length == 0)
            {
                return PostError.PostCategoriesEmpty;
            }

            int currentSectionID = 0;

            foreach (var category in Model.Categories)
            {
                var dbCategory = DB.Categories.FirstOrDefault(c => c.ID == category);
                if (dbCategory == null)
                {
                    return PostError.PostCategoriesIncorrect;
                }

                if (currentSectionID == 0)
                {
                    currentSectionID = dbCategory.Section.ID;
                }
                else if (dbCategory.Section.ID != currentSectionID)
                {
                    return PostError.PostCategoriesFromDifferentSections;
                }
            }

            return PostError.None;
        }

        #endregion
    }
}
